/**
  ******************************************************************************
  * @file           
  * @author    古么宁
  * @brief     iec101 示例程序 02
  * @note      此程序基于 01 示例，主要示范如何往总线添加总召响应和遥控响应，与 01 相比
  *            主要是需要新建一个响应注册函数 user_init() 并在 iec10x_bus_init() 的第
  *            四个参数传入这个函数，使其在进行基本的总线初始化后调用 user_init() 进行接口
  *            注册。
  ******************************************************************************
  *
  * COPYRIGHT(c) 2019 GoodMorning
  *
  ******************************************************************************
  */
/* Includes ---------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <termios.h>	/* POSIX 终端控制定义*/ 
#include <signal.h>
#include <sys/time.h>   //select time

#include "iec10x.h"

/* Private types ------------------------------------------------------------*/

/* Private macro ------------------------------------------------------------*/

/* Private variables --------------------------------------------------------*/

/** 新建全局总线变量 */
iec10x_t   demobus;

int ttyfd = 0;

/* Global  variables --------------------------------------------------------*/

/* Private function prototypes ----------------------------------------------*/

/* Gorgeous Split-line ------------------------------------------------------*/



void iec10x_puts(unsigned char * info , int len)
{
	if (ttyfd) {
		write(ttyfd,info,len);
	}
}


/**
  * @brief    串口设置
  * @param    fd       : 已打开的串口句柄
  * @param    nspeed   : 波特率，9600，115200 等
  * @param    nbits    : 数据宽度，7/8/9
  * @param    nevent   : 数据校验，'O'(奇)/'E'(偶)/'N'(无)
  * @param    nstop    : 停止位，1/2
  * @return   设置成功返回 0 ，否则返回其他
*/
static int usart_set(int fd, int nspeed, int nbits, char nevent, int nstop)
{
	struct termios newtio, oldtio;
	if(tcgetattr(fd, &oldtio)!=0) {
		perror("setup serial 1");
		return -1;
	}

	memset(&newtio, 0 , sizeof(newtio));

	newtio.c_cflag |= CLOCAL|CREAD;
	newtio.c_cflag &=~CSIZE;
	switch(nbits){
		case 7:
			newtio.c_cflag |= CS7;
			break;
		case 8:
			newtio.c_cflag |= CS8;
			break;
	}
	switch(nevent){
		case 'O':
			newtio.c_cflag |= PARENB;
			newtio.c_cflag |= PARODD;
 			newtio.c_iflag |= (INPCK|ISTRIP);
			break;
		case 'E':
			newtio.c_cflag |= PARENB;
			newtio.c_cflag &=~PARODD;
			newtio.c_iflag |=(INPCK|ISTRIP);
			break;
		case 'N':
			newtio.c_cflag &=~PARENB;
			break;
	}

	switch(nspeed){
		case 2400:
			cfsetospeed(&newtio, B2400);
			break;
		case 4800:
			cfsetospeed(&newtio, B4800);
			break;
		case 9600:
			cfsetispeed(&newtio, B9600);
			break;
		case 19200:
			cfsetispeed(&newtio, B19200);
			break;
		case 115200:
			cfsetispeed(&newtio, B115200);
			break;
		default :
			cfsetospeed(&newtio, B9600);
	}

	if(nstop == 1)
		newtio.c_cflag &=~CSTOPB;
	else 
	if(nstop == 2)
		newtio.c_cflag |=CSTOPB;

	newtio.c_iflag &= ~ICRNL;//禁止将输入中的回车翻译为新行 (除非设置了 IGNCR)
	newtio.c_iflag &= ~ISTRIP;//禁止将所有接收的字符裁减为7比特

	newtio.c_cc[VTIME]= 0;
	newtio.c_cc[VMIN] = 1;

	tcflush(fd, TCIFLUSH);
	if(tcsetattr(fd, TCSANOW, &newtio) != 0){
		perror("com set error.\r\n");
		return -1;
	}
	
	return 0;
}


void * hal_thread(void * arg)
{
	unsigned char databuf[512] = {0};
	int datalen = 0;
	ttyfd = open(arg,O_SYNC|O_RDWR|O_NOCTTY) ;
	if (ttyfd < 0) {
		printf("cannot open this usart device:%s\r\n",(char *)arg);
		exit(0) ;
	}

	if (0 != usart_set(ttyfd,9600,8,'N',1)) {
		printf("set serial error.\r\n");
		exit(0);
	}

	printf("%s :standby...\r\n",(char *)arg);
	while(1) {
		datalen = read(ttyfd,databuf,sizeof(databuf));
		if (datalen < 1) {
			printf("read error\r\n");
			exit(0);
		}

		/* 接受到的数据直接传给协议栈处理 */
		iec10x_recv(&demobus,databuf,datalen);
	}
}


void * iec10x_loop(void * arg)
{
	struct timeval timer100ms ;
	size_t time = 0;
	while(1) {
		timer100ms.tv_sec = 0;
		timer100ms.tv_usec = 100000 ;
		select(0, NULL, NULL, NULL, &timer100ms);
		time += 100;

		/* 处理协议栈的超时/定时 */
		iec10x_periodic_handle(time); 
	}
}




/**
  * @brief    用户层数据接口注册函数
  * @param    bus     : 目标总线
*/
void user_init(iec10x_t * bus)
{
	// 从机接收到主机的单点遥控命令响应
//	iec10x_devif_register(bus,C_SC_NA_1 ,NULL , NULL , SIQsctrl); // 单点遥控命令设备接口，
}



int main(int argc, char *argv[])
{
	iec101cfg_t cfg = {0};  // 总线初始化结构体

	pthread_t newpthread ;
	if (argc < 2) {
		printf("Usage:cmd [/dev/ttySx]\r\n");
		return -1;
	}

	#if 0
		/* 平衡模式下，适用于 kw2200 调试工具测试 */
		cfg.haltx       = iec10x_puts    ; // 指定总线硬件发送函数
		cfg.print       = printf         ; // 指定总线调试打印函数
		cfg.dbglevel    = IEC_DBG_MIDDLE ; // 指定总线打印等级为中等
		cfg.cmnaddr     = 1              ; // 公共地址
		cfg.lnkaddr     = 1              ; // 链路地址
		cfg.cmnaddrlen  = 2              ; // 公共地址长度
		cfg.causelen    = 2              ; // 传输原因长度
		cfg.lnkaddrlen  = 2              ; // 链路地址长度
		cfg.infoaddrlen = 3              ; // 信息结构体地址长度
		cfg.balance     = 1              ; // 平衡模式
		cfg.mode        = 0              ; // 为 0 时作从站
	#else
		/* 非平衡模式下，适用于 PMA 调试工具测试 */
		cfg.haltx       = iec10x_puts    ; // 指定总线硬件发送函数
		cfg.print       = printf         ; // 指定总线调试打印函数
		cfg.dbglevel    = IEC_DBG_MIDDLE ; // 指定总线打印等级为中等
		cfg.cmnaddr     = 1              ; // 公共地址
		cfg.lnkaddr     = 1              ; // 链路地址
		cfg.cmnaddrlen  = 1              ; // 公共地址长度
		cfg.causelen    = 1              ; // 传输原因长度
		cfg.lnkaddrlen  = 1              ; // 链路地址长度
		cfg.infoaddrlen = 2              ; // 信息结构体地址长度
		cfg.balance     = 0              ; // 非平衡模式
		cfg.mode        = 0              ; // 为 0 时作从站
	#endif

	/* 先对协议栈作整体的初始化 */
	iec10x_protocol_init();

	/* 用以上 cfg 结构体对 demobus 总线进行初始化 */
	iec10x_bus_init(&demobus,101,&cfg,NULL);

	/* 创建轮询进程处理协议栈的定时任务 */
	pthread_create(&newpthread,NULL,iec10x_loop,NULL);

	/* 创建硬件接收线程 */
	pthread_create(&newpthread,NULL,hal_thread,argv[1]);

	getchar();

	return 0;
}